package com.mycompany.myapp.service.impl;

import com.mycompany.myapp.domain.Commodity;
import com.mycompany.myapp.domain.OrderItem;
import com.mycompany.myapp.service.CommodityService;
import com.mycompany.myapp.service.OrderItemService;
import com.mycompany.myapp.service.OrderService;
import com.mycompany.myapp.domain.Order;
import com.mycompany.myapp.repository.OrderRepository;
import com.mycompany.myapp.service.dto.OrderDTO;
import com.mycompany.myapp.service.dto.OrderItemDTO;
import com.mycompany.myapp.service.dto.ReportDTO;
import com.mycompany.myapp.service.mapper.CommodityMapper;
import com.mycompany.myapp.service.mapper.OrderItemMapper;
import com.mycompany.myapp.service.mapper.OrderMapper;
import com.mycompany.myapp.web.rest.utils.DateUtil;
import com.mycompany.myapp.web.rest.utils.ResultObj;
import com.mycompany.myapp.web.rest.utils.TypeUtils;
import com.mycompany.myapp.web.rest.vm.OrderItemVM;
import com.mycompany.myapp.web.rest.vm.OrderVM;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.*;

/**
 * Service Implementation for managing {@link Order}.
 */
@Service
@Transactional
public class OrderServiceImpl implements OrderService {

    private final Logger log = LoggerFactory.getLogger(OrderServiceImpl.class);

    private final OrderRepository orderRepository;

    private final OrderMapper orderMapper;

    private final OrderItemService orderItemService;

    private final CommodityService commodityService;

    private final CommodityMapper commodityMapper;

    private final OrderItemMapper orderItemMapper;

    public OrderServiceImpl(OrderRepository orderRepository, OrderMapper orderMapper,
                            OrderItemService orderItemService, CommodityService commodityService,
                            CommodityMapper commodityMapper, OrderItemMapper orderItemMapper) {
        this.orderRepository = orderRepository;
        this.orderMapper = orderMapper;
        this.orderItemService = orderItemService;
        this.commodityService = commodityService;
        this.commodityMapper = commodityMapper;
        this.orderItemMapper = orderItemMapper;
    }

    /**
     * Save a order.
     *
     * @param orderDTO the entity to save.
     * @return the persisted entity.
     */
    @Override
    public OrderDTO save(OrderDTO orderDTO) {
        log.debug("Request to save Order : {}", orderDTO);
        Order order = orderMapper.toEntity(orderDTO);
        order = orderRepository.save(order);
        return orderMapper.toDto(order);
    }

    /**
     * Get all the orders.
     *
     * @return the list of entities.
     */
    @Override
    @Transactional(readOnly = true)
    public List<OrderDTO> findAll() {
        log.debug("Request to get all Orders");
        List<Order> orders = orderRepository.findAll();
        List<OrderItemDTO> orderItems = orderItemService.findAll();
        List<OrderDTO> orderDTOS = new ArrayList<>();
        for (Order order : orders) {
            OrderDTO orderDTO = orderMapper.toDto(order);
            orderDTO.removeAllOrderItem();
            for (OrderItemDTO orderItemDTO : orderItems) {
                if (orderItemDTO.getOrderId() == order.getId()) {
                    orderDTO.addOrderItem(orderItemDTO);
                }
            }
            orderDTOS.add(orderDTO);
        }
        return orderDTOS;
    }


    /**
     * Get one order by id.
     *
     * @param id the id of the entity.
     * @return the entity.
     */
    @Override
    @Transactional(readOnly = true)
    public Optional<OrderDTO> findOne(Long id) {
        log.debug("Request to get Order : {}", id);
        return orderRepository.findById(id)
            .map(orderMapper::toDto);
    }

    /**
     * Delete the order by id.
     *
     * @param id the id of the entity.
     */
    @Override
    public void delete(Long id) {
        log.debug("Request to delete Order : {}", id);
        orderRepository.deleteById(id);
    }

    public ResultObj createOrder(OrderVM orderVM) {
        Commodity commodity;
        OrderDTO orderDTO = new OrderDTO();
        OrderItemDTO orderItem;
        long now = System.currentTimeMillis();
        if (!TypeUtils.isEmpty(orderVM)) {
            if (!TypeUtils.isEmpty(orderVM.getOrderItems())) {
                orderDTO.setId(now);
                orderDTO.setTotal(orderVM.getTotal());
                orderDTO.setMessage(orderVM.getMessage());
                orderDTO.setCreateTime(DateUtil.getZoneDateTime());
                orderDTO.setName(orderVM.getName());
                orderDTO.setMobile(orderVM.getMobile());
                orderDTO.setProvince(orderVM.getProvince());
                orderDTO.setAddress(orderVM.getAddress());
                orderDTO.setSuserId(orderVM.getSuserId());
                orderDTO.setStatus(orderVM.getStatus());
                save(orderDTO);
                for (OrderItemVM orderItemVM : orderVM.getOrderItems()) {
                    orderItem = new OrderItemDTO();
                    orderItem.setOrderId(now);
                    orderItem.setCount(orderItemVM.getCount());
                    orderItem.setPrice(orderItemVM.getPrice());
                    orderItem.setTotal(orderItemVM.getTotal());
                    orderItem.setCommodity(commodityService.findCommodityById(orderItemVM.getCommodity()));
                    orderItemService.save(orderItem);
                    commodity = orderItem.getCommodity();
                    commodity.setCount(commodity.getCount() - orderItem.getCount());
                    commodityService.save(commodityMapper.toDto(commodity));
                }
                return ResultObj.backInfo(true, 200, "生成订单成功", null);
            } else {
                return ResultObj.backInfo(true, 200, "订单项为空", null);
            }
        } else {
            return ResultObj.backInfo(false, 200, "生成订单失败", null);
        }
    }


    public List<OrderDTO> findOrderByUserId(Long userId) {
        List<Order> orders = new ArrayList<>();
        if (!TypeUtils.isEmpty(userId)) {
            orders = orderRepository.findBySuserId(userId);
            for (Order order : orders) {
                order.setOrderItems(orderItemService.findByOrderId(order.getId()));
            }
        }
        return orderMapper.toDto(orders);
    }

    public List<OrderDTO> findAllUnfinishOrders(Long userId) {
        List<Order> orders = new ArrayList<>();
        List<OrderDTO> orderDTOS = new ArrayList<>();
        if (!TypeUtils.isEmpty(userId)) {
            orders = orderRepository.findBySuserIdAndStatus(userId, "未支付");
            List<OrderItemDTO> orderItems = orderItemService.findAll();
            for (Order order : orders) {
                OrderDTO orderDTO = orderMapper.toDto(order);
                orderDTO.removeAllOrderItem();
                for (OrderItemDTO orderItemDTO : orderItems) {
                    if (orderItemDTO.getOrderId() == order.getId()) {
                        orderDTO.addOrderItem(orderItemDTO);
                    }
                }
                orderDTOS.add(orderDTO);
            }
        }
        return orderDTOS;
    }

    public ResultObj payOrder(Long id) {
        if (!TypeUtils.isEmpty(id)) {
            Order order = orderRepository.getOne(id);
            if (!TypeUtils.isEmpty(order)) {
                order.setStatus("已支付");
                orderRepository.save(order);
                return ResultObj.backInfo(true, 200, "支付成功", null);
            }
        }
        return ResultObj.backInfo(false, 200, "支付失败", null);
    }

    public List<OrderDTO> findAllFinishOrder(Long userId) {
        List<Order> orders;
        List<OrderDTO> orderDTOS = new ArrayList<>();
        if (!TypeUtils.isEmpty(userId)) {
            orders = orderRepository.findBySuserIdAndStatus(userId, "已支付");
            List<OrderItemDTO> orderItems = orderItemService.findAll();
            for (Order order : orders) {
                OrderDTO orderDTO = orderMapper.toDto(order);
                orderDTO.removeAllOrderItem();
                for (OrderItemDTO orderItemDTO : orderItems) {
                    if (orderItemDTO.getOrderId() == order.getId()) {
                        orderDTO.addOrderItem(orderItemDTO);
                    }
                }
                orderDTOS.add(orderDTO);
            }
        }
        return orderDTOS;
    }

    public ResultObj deleteOrderById(Long orderId) {
        if (!TypeUtils.isEmpty(orderId)) {
            for (OrderItem orderItem : orderItemService.findByOrderId(orderId)) {
                orderItemService.delete(orderItem.getId());
            }
            delete(orderId);

            return ResultObj.back(200, "删除成功！", null);
        } else {
            return ResultObj.back(200, "订单id为空！", null);
        }
    }

    @Override
    public ResultObj findByParams(int pageNum, int pageSize, String query) {
        Pageable pageable = PageRequest.of(pageNum - 1, pageSize);
        Page<Order> orders = null;
        try {
            if (query == "" || query == null) {
                orders = orderRepository.findAll(pageable);
            } else {
                long param = Long.parseLong(query);
                orders = orderRepository.findByParams(param, pageable);
            }
        } catch (Exception e) {
            orders = orderRepository.findAll(pageable);
        } finally {
            if (TypeUtils.isEmpty(orders.getContent())) {
                return ResultObj.back(201, orders, orders.getTotalElements());
            }
            List<OrderDTO> orderDTOS = new ArrayList<>();
            List<OrderItemDTO> orderItems = orderItemService.findAll();
            for (Order order : orders) {
                OrderDTO orderDTO = orderMapper.toDto(order);
                orderDTO.removeAllOrderItem();
                for (OrderItemDTO orderItemDTO : orderItems) {
                    if (orderItemDTO.getOrderId() == order.getId()) {
                        orderDTO.addOrderItem(orderItemDTO);
                    }
                }
                orderDTOS.add(orderDTO);
            }
            return ResultObj.back(200, orderDTOS, orders.getTotalElements());
        }
    }

    @Override
    public ResultObj updateOrderInfo(OrderDTO orderDTO) {
        if (orderRepository.findById(orderDTO.getId()).isPresent()) {
            orderRepository.findById(orderDTO.getId()).ifPresent(order -> {
                order.setProvince(orderDTO.getProvince());
                order.setAddress(orderDTO.getAddress());
                order.setStatus(orderDTO.getStatus());
                orderRepository.save(order);
            });
            return ResultObj.back(200, "更新成功", null);
        }
        return ResultObj.back(201, "更新失败", null);
    }

    @Override
    public ResultObj selectAllByToDay() {

        Specification<Order> specification = new Specification<Order>(){
            @Override
            public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate predicate = cb.conjunction();
                LocalDate localDate = DateUtil.getZoneDateTime().toLocalDate();
                ZonedDateTime createDate = localDate.atStartOfDay(ZoneOffset.UTC);
                //Date createDate = java.sql.Date.valueOf(localDate);
                localDate = localDate.plusDays(1);
                ZonedDateTime endDate = localDate.atStartOfDay(ZoneOffset.UTC);
                predicate.getExpressions().add(cb.between(root.get("createTime"), createDate, endDate));
                return predicate;
            }
        };
        List<Order> list = orderRepository.findAll(specification);
        Double total = 0.0;
        for (Order order: list){
            total+=order.getTotal();
        }
        LocalDate data = DateUtil.getZoneDateTime().toLocalDate();
        ReportDTO reportDTO = new ReportDTO();
        reportDTO.setDate(data);
        reportDTO.setTotalMoney(total);
        return ResultObj.back(200,reportDTO);
    }

    private static Specification<Order> getTimeParam(ZonedDateTime startTime,ZonedDateTime endTime){

        Specification<Order> specification = new Specification<Order>() {
            @Override
            public Predicate toPredicate(Root<Order> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate predicate = cb.conjunction();
                predicate.getExpressions().add(cb.between(root.get("createTime"), startTime, endTime));
                return predicate;
            }
        };
        return specification;
    }
    @Override
    public ResultObj selectAllByWeek() {
        List<ReportDTO> reportDTOS = new ArrayList<>();
        for (int i=6;i>=0;i--){
            LocalDate localDate = DateUtil.getZoneDateTime().toLocalDate();
            localDate = localDate.minusDays(i);
            ZonedDateTime createDate = localDate.atStartOfDay(ZoneOffset.UTC);
            //Date createDate = java.sql.Date.valueOf(localDate);
            localDate = localDate.plusDays(1);
            ZonedDateTime endDate = localDate.atStartOfDay(ZoneOffset.UTC);

            List<Order> list = orderRepository.findAll(getTimeParam(createDate,endDate));
            Double total = 0.0;
            for (Order order: list){
                total+=order.getTotal();
            }
            ReportDTO reportDTO = new ReportDTO();
            reportDTO.setDate(createDate.toLocalDate());
            reportDTO.setTotalMoney(total);
            reportDTOS.add(reportDTO);
        }
        return ResultObj.back(200,reportDTOS);
    }

    @Override
    public ResultObj selectAllByAnyTime(String startTime, String endTime) {
        Date createDate = DateUtil.getDate(startTime,"yyyy-MM-dd");
        ZonedDateTime start = DateUtil.toZonedDateTime(createDate);
        Date endDate = DateUtil.getDate(endTime,"yyyy-MM-dd");
        ZonedDateTime end = DateUtil.toZonedDateTime(endDate);
        long count = DateUtil.getAccesstime(endDate,createDate)/(60*24);
        List<ReportDTO> reportDTOS = new ArrayList<>();
        for (int i = 0; i<=(int)count; i++){
            LocalDate localDate = start.toLocalDate();
            localDate = localDate.plusDays(i);
            ZonedDateTime zstart = localDate.atStartOfDay(ZoneOffset.UTC);
            localDate = localDate.plusDays(1);
            ZonedDateTime zend= localDate.atStartOfDay(ZoneOffset.UTC);

            List<Order> list = orderRepository.findAll(getTimeParam(zstart,zend));
            Double total = 0.0;
            for (Order order: list){
                total+=order.getTotal();
            }
            //LocalDate data = end.toLocalDate().plusDays(i);
            ReportDTO reportDTO = new ReportDTO();
            reportDTO.setDate(zstart.toLocalDate());
            reportDTO.setTotalMoney(total);
            reportDTOS.add(reportDTO);
        }
        return ResultObj.back(200,reportDTOS);
    }
}
